package com.uinnova.product.eam.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.framework.Local;
import com.binary.framework.bean.SimpleUser;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.WorkbenchChargeDone;
import com.uinnova.product.eam.constant.*;
import com.uinnova.product.eam.dao.ArchReviewInfoDao;
import com.uinnova.product.eam.dao.CirCulateRecordDao;
import com.uinnova.product.eam.dto.*;
import com.uinnova.product.eam.feign.workable.FlowableFeign;
import com.uinnova.product.eam.feign.workable.entity.*;
import com.uinnova.product.eam.local.ArchReviewSubmitContext;
import com.uinnova.product.eam.local.ArchReviewSubmitContextValue;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.cj.domain.TemplateType;
import com.uinnova.product.eam.model.constants.FlowableConstant;
import com.uinnova.product.eam.model.es.ArchReviewInfo;
import com.uinnova.product.eam.model.es.ArchReviewOrgDetailConf;
import com.uinnova.product.eam.model.es.CirCulateRecord;
import com.uinnova.product.eam.service.*;
import com.uinnova.product.eam.service.asset.BmConfigSvc;
import com.uinnova.product.eam.service.cj.service.DeliverableTemplateService;
import com.uinnova.product.eam.service.cj.service.PlanDesignInstanceService;
import com.uinnova.product.eam.service.cj.service.ShareService;
import com.uinnova.product.eam.vo.*;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CiGroupPage;
import com.uino.api.client.permission.IOrgApiSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.permission.base.SysOrg;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.business.UserInfo;
import com.uino.bean.permission.query.CSysOrg;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.permission.ESUserSvc;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Log4j2
public class ArchReviewServiceImpl implements ArchReviewService {

    private static final SimpleDateFormat NUM_DF = new SimpleDateFormat("yyyyMMdd");
    private static final SimpleDateFormat STR_DF = new SimpleDateFormat("yyyy-MM-dd");
    @Autowired
    private PlanDesignInstanceService planDesignInstanceService;
    @Autowired
    private AXEAConfService axeaConfService;
    @Autowired
    private IamsCISwitchSvc ciSwitchSvc;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private SystemService systemService;
    @Autowired
    private ShareService shareService;
    @Autowired
    private IUserApiSvc userApiSvc;
    @Autowired
    private IOrgApiSvc orgService;
    @Autowired
    private ArchReviewInfoDao archReviewInfoDao;
    @Autowired
    private FlowableFeign flowableFeign;
    @Autowired
    private ArchReviewOrgConfService orgConfService;
    @Autowired
    private DeliverableTemplateService deliverableTemplateService;
    @Autowired
    private CirCulateRecordDao cirCulateRecordDao;
    @Autowired
    private IEamNoticeService noticeService;
    @Autowired
    private MeasureFlowRecordService measureFlowRecordService;
    @Autowired
    private MeasureEvaluateService measureEvaluateService;
    @Autowired
    private WorkbenchChargeDoneSvc workbenchChargeDoneSvc;
    @Resource
    private BmConfigSvc bmConfigSvc;
    @Resource
    private ESUserSvc esUserSvc;

    @Override
    public ArchReviewSubmitBeforeInfo submitBeforeInfo(Long planId) {
        if (planId == null) {
            return new ArchReviewSubmitBeforeInfo();
        }
        PlanDesignInstance plan = planDesignInstanceService.getPlanDesignInstance(planId);
        Assert.notNull(plan, "未找到架构方案");
        Long planBindAssetClassId = plan.getDefaultSystemClassId();
        if (planBindAssetClassId == null) {
            log.error("架构方案关联资产类型不支持提交评审，请联系管理员处理--方案无绑定资产");
            throw new BinaryException("架构方案关联资产类型不支持提交评审，请联系管理员处理");
        }
        //校验模板类型&&模板绑定资产类型
        ArchReviewConf archReviewConf = axeaConfService.getArchReviewConf();
        if (!archReviewConf.getArchReviewTemplateTypeId().equals(plan.getTypeId())) {
            log.error("架构方案关联资产类型不支持提交评审，请联系管理员处理--方案模板非'架构方案'类型");
            throw new BinaryException("架构方案关联资产类型不支持提交评审，请联系管理员处理");
        }
        AXEAClassMapping axeaClassMapping = axeaConfService.getSystemClassMapping();
        if (!archReviewConf.getProjectClassId().equals(planBindAssetClassId)
                && !axeaClassMapping.getSystemClassId().equals(planBindAssetClassId)) {
            log.error("架构方案关联资产类型不支持提交评审，请联系管理员处理--方案模板关联资产类型非项目/系统");
            throw new BinaryException("架构方案关联资产类型不支持提交评审，请联系管理员处理");
        }
        ArchReviewSubmitBeforeInfo beforeInfo = new ArchReviewSubmitBeforeInfo();
        boolean project = archReviewConf.getProjectClassId().equals(planBindAssetClassId);
        beforeInfo.setItemType(project ? ItemType.PROJECT : ItemType.SYSTEM);
        String label = getPlanBindAssetName(plan);
        if (project) {
            beforeInfo.setProjectCiCode(plan.getDefaultSystemCiCode());
            SimpProjectInfo projectInfo = new SimpProjectInfo();
            projectInfo.setCiCode(plan.getDefaultSystemCiCode());
            projectInfo.setLabel(label);
            beforeInfo.setProjectInfo(projectInfo);
        } else {
            beforeInfo.setSysCiCodes(Arrays.asList(plan.getDefaultSystemCiCode()));
            SimpSystemInfo systemInfo = new SimpSystemInfo();
            systemInfo.setCiCode(plan.getDefaultSystemCiCode());
            systemInfo.setLabel(label);
            beforeInfo.setSystemInfos(Arrays.asList(systemInfo));
        }
        SimpPlanInfo planInfo = new SimpPlanInfo();
        planInfo.setId(plan.getId());
        planInfo.setName(plan.getName());
        beforeInfo.setPlans(Arrays.asList(planInfo));
        return beforeInfo;
    }

    private String getPlanBindAssetName(PlanDesignInstance plan) {
        if (CollectionUtils.isEmpty(plan.getCiCodeList())) {
            return "-";
        }
        List<ESCIInfo> ciInfoList = ciSwitchSvc.getCiByCodes(plan.getCiCodeList(), null, LibType.DESIGN);
        List<String> ciLabelList = ciInfoList.stream()
                .filter(esciInfo -> !BinaryUtils.isEmpty(esciInfo.getCiLabel()))
                .map(esciInfo -> esciInfo.getCiLabel()
                        .replace("[", "")
                        .replace("]", "").replaceAll("\"", ""))
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ciLabelList)) {
            return "-";
        }
        return StringUtils.join(ciLabelList, "-");
    }

    @Override
    public ArchReviewSubmitRes submit(ArchReviewSubmitReq req) {
        log.info("架构方案提交评审[params:{}]", JSON.toJSONString(req));
        //入参校验并实例化上下文信息
        checkAndPrepareParams(req);
        //方案发布校验
        ArchReviewSubmitRes res = checkPlans();
        if (!res.getResult()) {
            return res;
        }
        //维护方案评审信息
        initArchReviewInfo(req);
        //方案数据预处理
        List<PlanDesignInstance> plans = planDesignInstanceService.gtPlanSubmit(req.getPlanIds(), true);
        //发起流程
        String processInstanceId = startFlow();
        //保存架构评审信息
        ArchReviewInfo reviewInfo = ArchReviewSubmitContext.getContext().getReviewInfo();
        reviewInfo.setProcessInstanceId(processInstanceId);
        archReviewInfoDao.saveOrUpdate(reviewInfo);
        //维护流程度量记录
        MeasureFlowRecordSaveVo saveVo = new MeasureFlowRecordSaveVo();
        saveVo.setArchReviewId(reviewInfo.getId());
        saveVo.setAction(1);
        measureFlowRecordService.save(saveVo);
        //保存方案信息
        planDesignInstanceService.saveOrUpdateBatch(plans);
        // 如果关联资产是项目 则更新项目的 架构评审状态 和 架构方案地址
        updateProject(req.getItemType(), req.getProjectCiCode(), plans, ArchReviewState.REVIEWING, processInstanceId, String.valueOf(reviewInfo.getCreateTime()), "");
        ArchReviewSubmitContext.release();
        return res;
    }

    private String startFlow() {
        ArchReviewSubmitContextValue context = ArchReviewSubmitContext.getContext();
        ArchReviewInfo reviewInfo = context.getReviewInfo();
        //查团队架构评审配置
        Long belongOrgId = reviewInfo.getBelongOrgId();
        ArchReviewOrgDetailConf orgDetailConf = orgConfService.getLatestByOrgId(belongOrgId);
        log.info("查团队架构评审配置:orgId:{}, orgDetailConf:{}", belongOrgId, JSON.toJSONString(orgDetailConf));
        //发起流程
        ProcessRequest processRequest = new ProcessRequest();
        processRequest.setProcessDefinitionKey(FlowableConstant.GT_TECHNICAL_SCHEME_APPROVE);
        processRequest.setProcessInstanceName(getFlowName(reviewInfo));
        processRequest.setBusinessKey(reviewInfo.getId().toString());
        String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        processRequest.setUserId(curUserLoginCode);
        processRequest.setOwner(curUserLoginCode);
        PorcessResponse response = flowableFeign.startProcessBindAssignee(processRequest);
        if (StringUtils.isBlank(response.getTaskId())) {
            log.error("架构评审流程发起失败:params[{}],res:{}", JSON.toJSONString(processRequest), JSON.toJSONString(response));
            throw new BinaryException("架构评审流程发起失败");
        }

        TaskRequest taskRequest = new TaskRequest();
        taskRequest.setAction(FLOWACTION.ACCETP);
        taskRequest.setTaskId(response.getTaskId());
        Map<String, Object> taskEchoVariables = new HashMap<>();
        taskEchoVariables.put("flowBatch", 1);
        taskRequest.setTaskEchoVariables(taskEchoVariables);
        //无团队评审
        if (orgDetailConf == null) {
            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("orgApprove", "jump");
            taskRequest.setRouterVariables(stringObjectHashMap);
            flowableFeign.completeTask(taskRequest);
            return response.getProcessInstanceId();
        }
        //或签
        if (ArchReviewOrgRule.OR_SIGN.equals(orgDetailConf.getRule())) {
            HashMap<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("orgApprove", "orSign");
            stringObjectHashMap.put("assigneeList", orgDetailConf.getPersions());
            taskRequest.setRouterVariables(stringObjectHashMap);
            flowableFeign.completeTask(taskRequest);
            return response.getProcessInstanceId();
        }
        //会签
        List<ChildProcessRequest> childProcessRequests = new ArrayList<>();
        //会签开启子流程审明一个id，用于会签子流程驳回时消息不重发
        Long countersignId = ESUtil.getUUID();
        for (String persion : orgDetailConf.getPersions()) {
            ChildProcessRequest childProcessRequest = new ChildProcessRequest();
            childProcessRequest.setChildBusinessKey(reviewInfo.getId() + "_CHILD_ORG_SUB_" + persion);
            Map<String, Object> stringObjectHashMap = new HashMap<>();
            stringObjectHashMap.put("assignee", persion);
            stringObjectHashMap.put("countersignId", countersignId);
            childProcessRequest.setChildVariable(stringObjectHashMap);
            childProcessRequests.add(childProcessRequest);
        }
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("orgApprove", "countersign");
        stringObjectHashMap.put("activeChildProcess", "true");
        taskRequest.setRouterVariables(stringObjectHashMap);
        taskRequest.setChildProcessRequestList(childProcessRequests);
        flowableFeign.completeTask(taskRequest);
        return response.getProcessInstanceId();
    }

    private String getFlowName(ArchReviewInfo reviewInfo) {
        String reviewType = "";
        if (ArchReviewType.APPROVAL.equals(reviewInfo.getReviewType())) {
            reviewType = "(立项)";
        } else if (ArchReviewType.ARCH_CHANGE.equals(reviewInfo.getReviewType())) {
            reviewType = "(架构变更)";
        }
        return "架构预评审" + reviewType + "-".concat(getTitle(reviewInfo));
    }

    private String getTitle(ArchReviewInfo reviewInfo) {
        if (ItemType.PROJECT.equals(reviewInfo.getItemType())) {
            return reviewInfo.getProjectInfo().getLabel();
        }
        List<SimpSystemInfo> systemInfos = reviewInfo.getSystemInfos();
        String systemName = "";
        if (systemInfos.size() <= 5) {
            systemName = systemInfos.stream().map(SimpSystemInfo::getLabel).collect(Collectors.joining(","));
        } else {
            systemName = systemInfos.subList(0, 5).stream().map(SimpSystemInfo::getLabel)
                    .collect(Collectors.joining(",")).concat("等系统");
        }
        return systemName;
    }

    /**
     * 提交架构评审入参校验并实例化上下文信息
     * @param req
     */
    private void checkAndPrepareParams(ArchReviewSubmitReq req) {
        ArchReviewSubmitContext.init();
        Assert.notNull(req, "缺少必填项信息");
        Assert.notNull(req.getReviewFrom(), "评审来源不能为空");
        Assert.notNull(req.getReviewType(), "评审类别不能为空");
        Assert.notNull(req.getItemType(), "事项类型不能为空");
        List<Long> planIds = req.getPlanIds().stream().filter(Objects::nonNull).collect(Collectors.toList());
        req.setPlanIds(planIds);
        Assert.notEmpty(req.getPlanIds(), "评审方案ids不能为空");
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(req.getPlanIds());
        if (CollectionUtils.isEmpty(plans) || req.getPlanIds().size() != plans.size()) {
            throw new BinaryException("方案已删除或协作权限已撤回，请刷新列表重试");
        }
        List<SimpPlanInfo> planInfos = EamUtil.copy(plans, SimpPlanInfo.class);
        SimpProjectInfo projectInfo = null;
        List<SimpSystemInfo> systemInfos = null;
        switch (req.getItemType()) {
            case PROJECT:
                Assert.notNull(req.getProjectCiCode(), "项目ciCode不能为空");
                List<SimpProjectInfo> projectInfos = projectService.queryByCiCodes(Arrays.asList(req.getProjectCiCode()));
                Assert.notEmpty(projectInfos, "未找到项目信息");
                projectInfo = projectInfos.get(0);
                break;
            case SYSTEM:
                Assert.notEmpty(req.getSysCiCodes(), "系统ciCodes不能为空");
                systemInfos = systemService.queryByCiCodes(req.getSysCiCodes());
                if (CollectionUtils.isEmpty(systemInfos) || systemInfos.size() != req.getSysCiCodes().size()) {
                    throw new BinaryException("未找到系统信息，请刷新列表重试");
                }
                break;
            default:
                throw new BinaryException("不支持的事项类型");
        }
        //校验评审方案与"项目"/"系统"是否匹配或有权限
        checkReviewPlans(req, plans);
        //校验提交自架构方案的评审信息
        checkAndPrepareParamsCaseFromArchPlan(req);
        Assert.notNull(req.getBuildType(), "建设类型不能为空");
        Assert.notNull(req.getBuildMode(), "建设模式不能为空");
        Assert.notNull(req.getCustomizeSys(), "是否为定制化系统不能为空");
        Assert.notNull(req.getIncludeBizTrans(), "是否涉及业务改造不能为空");
        Assert.notNull(req.getIncludeArchTrans(), "是否涉及架构变更不能为空");
        if (req.getIncludeArchTrans()) {
            Assert.notNull(req.getTransLevel(), "变更级别不能为空");
            if (req.getCustomizeSys() && !TransLevel.customizeTransLevelString().contains(req.getTransLevel().toString())) {
                throw new BinaryException("定制化系统变更级别不匹配");
            }
            if (!req.getCustomizeSys() && !TransLevel.unCustomizeTransLevelString().contains(req.getTransLevel().toString())) {
                throw new BinaryException("非定制化系统变更级别不匹配");
            }
        }
        Assert.notNull(req.getProjectManager(), "项目经理不能为空");
        SysUser projectManage = getUserByLoginCode(req.getProjectManager());
        Assert.notNull(projectManage, "项目经理不存在");
        Assert.notNull(req.getBelongOrgId(), "所属团队不能为空");
        SysOrg sysOrg = getOrgById(req.getBelongOrgId());
        Assert.notNull(sysOrg, "所属团队不存在");
        SysOrg parentOrg = getOrgById(sysOrg.getParentOrgId());
        Assert.notNull(sysOrg, "所属团队父团队不存在");
        Assert.notNull(req.getReviewDesc(), "评审说明不能为空");
        //维护上下文信息
        ArchReviewSubmitContextValue contextValue = ArchReviewSubmitContext.getContext();
        contextValue.setPlanInfos(planInfos);
        contextValue.setProjectInfo(projectInfo);
        contextValue.setSystemInfos(systemInfos);
        contextValue.setProjectManagerName(projectManage.getUserName());
        contextValue.setBelongOrgName(parentOrg.getOrgName().concat("/").concat(sysOrg.getOrgName()));
        ArchReviewSubmitContext.setContext(contextValue);
    }

    private SysUser getUserByLoginCode(String loginCode) {
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodeEqual(loginCode);
        List<SysUser> sysUsers = userApiSvc.getSysUserByCdt(cdt);
        return CollectionUtils.isEmpty(sysUsers) ? null : sysUsers.get(0);
    }

    private UserInfo getUserInfoByLoginCode(String loginCode) {
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodeEqual(loginCode);
        List<UserInfo> userInfos = userApiSvc.getUserInfoByCdt(cdt, true);
        return CollectionUtils.isEmpty(userInfos) ? null : userInfos.get(0);
    }

    private SysOrg getOrgById(Long orgId) {
        CSysOrg query = new CSysOrg();
        query.setId(orgId);
        List<SysOrg> sysOrgs = orgService.queryListByCdt(query);
        return CollectionUtils.isEmpty(sysOrgs) ? null : sysOrgs.get(0);
    }

    /**
     * 校验评审方案与"项目"/"系统"是否匹配或有权限
     * @param req
     * @param plans
     */
    private void checkReviewPlans(ArchReviewSubmitReq req, List<PlanDesignInstance> plans) {
        ReviewPlanReq canSelectPlanReq = new ReviewPlanReq();
        canSelectPlanReq.setItemType(req.getItemType());
        List<String> selectedCiCodes = ItemType.PROJECT.equals(req.getItemType()) ? Arrays.asList(req.getProjectCiCode()) : req.getSysCiCodes();
        canSelectPlanReq.setCiCodes(selectedCiCodes);
        canSelectPlanReq.setPageNum(1);
        canSelectPlanReq.setPageSize(99999);
        //可选择的评审方案列表
        Page<SimpPlanInfo> canSelectPlanPage = reviewPlans(canSelectPlanReq);
        Set<Long> canSelectPlanIds = new HashSet<>();
        if (!CollectionUtils.isEmpty(canSelectPlanPage.getData())) {
            canSelectPlanIds = canSelectPlanPage.getData().stream().map(SimpPlanInfo::getId).collect(Collectors.toSet());
        }
        String itemTypeName = ItemType.PROJECT.equals(req.getItemType()) ? "项目" : "系统";
        for (PlanDesignInstance plan : plans) {
            if (!canSelectPlanIds.contains(plan.getId())) {
                log.error("方案[{}]暂不支持评审[planInfo:{}]", plan.getName(), JSON.toJSONString(plan));
                throw new BinaryException("方案[" + plan.getName() + "]暂不支持评审，请依次核查方案状态、模板类型/方案关联资产类型/方案绑定资产是否在" + itemTypeName + "名称内");
            }
        }
    }

    /**
     * 校验提交自架构方案的评审信息
     * @param req
     */
    private void checkAndPrepareParamsCaseFromArchPlan(ArchReviewSubmitReq req) {
        if (!ReviewFrom.ARCH_PLAN.equals(req.getReviewFrom())) {
            return;
        }
        Assert.notNull(req.getArchPlanId(), "评审来源为架构方案时的方案id不能为空");
        Long planId = req.getArchPlanId();
        ArchReviewSubmitBeforeInfo submitBeforeInfo = submitBeforeInfo(planId);
        //上下文维护评审提交前置信息
        ArchReviewSubmitContextValue contextValue = ArchReviewSubmitContext.getContext();
        contextValue.setSubmitBeforeInfo(submitBeforeInfo);
        ArchReviewSubmitContext.setContext(contextValue);
        if (!submitBeforeInfo.getItemType().equals(req.getItemType())) {
            throw new BinaryException("事项类型不允许修改");
        }
        //校验下方案在不在评审方案内
        if (!req.getPlanIds().contains(planId)) {
            throw new BinaryException("评审方案[" + submitBeforeInfo.getPlans().get(0).getName() + "]不允许删除");
        }
        //事项类型是项目
        if (ItemType.PROJECT.equals(req.getItemType())) {
            //校验下提交的项目是否是方案关联的项目资产
            if (!submitBeforeInfo.getProjectCiCode().equals(req.getProjectCiCode())) {
                throw new BinaryException("项目ciCode与方案管理资产不对应，请刷新页面重试");
            }
        }
        //如果事项类型是系统，校验下架构方案关联的系统是否在提交的系统列表里；
        if (ItemType.SYSTEM.equals(req.getItemType())) {
            if (!req.getSysCiCodes().contains(submitBeforeInfo.getSysCiCodes().get(0))) {
                throw new BinaryException("系统名称[" + submitBeforeInfo.getSystemInfos().get(0).getLabel() + "]不允许删除");
            }
        }
    }

    /**
     * 评审方案必填项校验
     * @return
     */
    private ArchReviewSubmitRes checkPlans() {
        ArchReviewSubmitContextValue contextValue = ArchReviewSubmitContext.getContext();
        List<SimpPlanInfo> planInfos = contextValue.getPlanInfos();
        List<SimpPlanInfo> checkUnPassPlans = new ArrayList<>();
        for (SimpPlanInfo planInfo : planInfos) {
            Map<String,Object> checkResult = planDesignInstanceService.checkPlan(planInfo.getId());
            if (Boolean.FALSE.equals(checkResult.get("result"))
                    || Boolean.FALSE.equals(checkResult.get("assertDirExist"))) {
                checkUnPassPlans.add(planInfo);
            }
        }
        return new ArchReviewSubmitRes(CollectionUtils.isEmpty(checkUnPassPlans), checkUnPassPlans);
    }

    /**
     * 维护方案评审信息
     * @param req
     */
    private void initArchReviewInfo(ArchReviewSubmitReq req) {
        ArchReviewSubmitContextValue context = ArchReviewSubmitContext.getContext();
        ArchReviewInfo reviewInfo = new ArchReviewInfo();
        reviewInfo.setId(ESUtil.getUUID());
        reviewInfo.setCreatorCode(SysUtil.getCurrentUserInfo().getLoginCode());
        reviewInfo.setCreateTime(ESUtil.getNumberDateTime());
        reviewInfo.setModifierCode(SysUtil.getCurrentUserInfo().getLoginCode());
        reviewInfo.setModifyTime(ESUtil.getNumberDateTime());
        reviewInfo.setReviewFrom(req.getReviewFrom());
        reviewInfo.setReviewType(req.getReviewType());
        reviewInfo.setItemType(req.getItemType());
        reviewInfo.setProjectCiCode(req.getProjectCiCode());
        reviewInfo.setProjectInfo(context.getProjectInfo());
        reviewInfo.setSysCiCodes(req.getSysCiCodes());
        reviewInfo.setSystemInfos(context.getSystemInfos());
        reviewInfo.setPlanIds(req.getPlanIds());
        reviewInfo.setPlanInfos(context.getPlanInfos());
        reviewInfo.setBuildType(req.getBuildType());
        reviewInfo.setBuildMode(req.getBuildMode());
        reviewInfo.setCustomizeSys(req.getCustomizeSys());
        reviewInfo.setIncludeBizTrans(req.getIncludeBizTrans());
        reviewInfo.setIncludeArchTrans(req.getIncludeArchTrans());
        reviewInfo.setTransLevel(req.getTransLevel());
        reviewInfo.setProjectManager(req.getProjectManager());
        reviewInfo.setProjectManagerName(context.getProjectManagerName());
        reviewInfo.setBelongOrgId(req.getBelongOrgId());
        reviewInfo.setBelongOrgName(context.getBelongOrgName());
        reviewInfo.setRefLink(req.getRefLink());
        reviewInfo.setReviewDesc(req.getReviewDesc());
        reviewInfo.setState(ArchReviewState.REVIEWING);
        context.setReviewInfo(reviewInfo);
        ArchReviewSubmitContext.setContext(context);
    }

    @Override
    public Page<SimpPlanInfo> reviewPlans(ReviewPlanReq req) {
        Assert.notNull(req, "分页参数不能为空");
        Assert.notNull(req.getPageNum(), "第几页不能为空");
        Assert.notNull(req.getPageSize(), "每页显示记录数不能为空");
        if (req.getItemType() == null || CollectionUtils.isEmpty(req.getCiCodes())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0, 0, new ArrayList<>());
        }
        //(1)	若“事项类型”选择“项目”，则，下拉列表内可选方案需符合如下校验：
        //    ①	方案状态为：草稿、已发布
        //    ②	本人创建，或拥有“可发布”权限协作方案
        //    ③	方案“关联资产类型”为“项目”
        //    ④	页面内“项目名称”和方案“关联资产名称”一致
        //    ⑤	方案模板类型为“架构方案”
        //(2)	若“事项类型”选择“系统”，则，下拉列表内可选方案需符合如下校验：
        //    ①	方案状态为：草稿、已发布
        //    ②	本人创建，或拥有“可发布”权限协作方案
        //    ③	方案“关联资产类型”为“系统”
        //    ④	方案“关联资产名称”，在页面“系统名称”已选项
        //    ⑤	方案模板类型为“架构方案”
        ArchReviewConf archReviewConf = axeaConfService.getArchReviewConf();
        AXEAClassMapping axeaClassMapping = axeaConfService.getSystemClassMapping();
        BoolQueryBuilder planQuery = QueryBuilders.boolQuery();
        //私有库视图
        planQuery.must(QueryBuilders.termQuery("assetsType", 1));
        //本人创建，或拥有“可发布”权限协作方案
        String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        BoolQueryBuilder permissionQuery = QueryBuilders.boolQuery();
        List<Long> publishPermissionPlanIds = shareService.getPublishPermissPlanIds(curUserLoginCode);
        if (!CollectionUtils.isEmpty(publishPermissionPlanIds)) {
            permissionQuery.should(QueryBuilders.termsQuery("id", publishPermissionPlanIds));
        }
        permissionQuery.should(QueryBuilders.termQuery("creatorCode.keyword", curUserLoginCode));
        planQuery.must(permissionQuery);
        //方案状态
        planQuery.must(QueryBuilders.termsQuery("status.keyword", Arrays.asList("draft","published")));
        //"架构方案"模板类型
        planQuery.must(QueryBuilders.termQuery("typeId", archReviewConf.getArchReviewTemplateTypeId()));
        //关联资产分类
        Long bindAssertClassId = ItemType.PROJECT.equals(req.getItemType()) ?
                archReviewConf.getProjectClassId() : axeaClassMapping.getSystemClassId();
        planQuery.must(QueryBuilders.termQuery("defaultSystemClassId", bindAssertClassId));
        //绑定资产
        planQuery.must(QueryBuilders.termsQuery("defaultSystemCiCode.keyword", req.getCiCodes()));
        //方案名称模糊检索
        if (StringUtils.isNotBlank(req.getKeyword())) {
            planQuery.must(QueryBuilders.wildcardQuery("name.keyword", "*" + req.getKeyword() + "*"));
        }
        List<SortBuilder<?>> sorts = new ArrayList<>();
        FieldSortBuilder modifyTimeSortBuilder = SortBuilders.fieldSort("modifyTime").order(SortOrder.DESC);
        sorts.add(modifyTimeSortBuilder);
        Page<PlanDesignInstance> page = planDesignInstanceService.getSortListByQuery(req.getPageNum(), req.getPageSize(), planQuery, sorts);
        if (CollectionUtils.isEmpty(page.getData())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), page.getTotalRows(), page.getTotalPages(), new ArrayList<>());
        }
        List<SimpPlanInfo> planInfos = new ArrayList<>();
        for (PlanDesignInstance planDesignInstance : page.getData()) {
            SimpPlanInfo planInfo = new SimpPlanInfo();
            planInfo.setId(planDesignInstance.getId());
            planInfo.setName(planDesignInstance.getName());
            planInfos.add(planInfo);
        }
        return new Page<>(req.getPageNum(), req.getPageSize(), page.getTotalRows(), page.getTotalPages(), planInfos);
    }

    @Override
    public ArchReviewInfo getById(Long id) {
        return archReviewInfoDao.getById(id);
    }

    @Override
    public void updateFeatureReviewType(Long id, FeatureReviewType featureReviewType, String stage) {
        Assert.notNull(id, "架构评审id不能为空");
        if (StringUtils.isBlank(stage)) {
            throw new BinaryException("流程节点不能为空");
        }
        ArchReviewInfo archReviewInfo = getById(id);
        Assert.notNull(archReviewInfo, "未找到架构评审信息");
        switch (stage) {
            case FlowableConstant.PRE_REVIEW_OFFICER:
                archReviewInfo.setOfficeReviewType(featureReviewType);
                archReviewInfo.setRotaReviewType(null);
                archReviewInfo.setFeatureReviewType(featureReviewType);
                break;
            case FlowableConstant.PRE_REVIEW_ROTA:
                archReviewInfo.setRotaReviewType(featureReviewType);
                archReviewInfo.setFeatureReviewType(featureReviewType);
                break;
            case FlowableConstant.PRE_REVIEW_FINAL_EXPERT:
                if (FeatureReviewType.EXPERT.equals(featureReviewType)) {
                    throw new BinaryException("节点[预评审-终审]'后续评审'不支持选择'预评审-专家初审'");
                }
                archReviewInfo.setExpertReviewType(featureReviewType);
                archReviewInfo.setFeatureReviewType(featureReviewType);
                break;
            default:
                throw new BinaryException("仅预评审-值班人评审、预评审-值班复核、预评审-终审节点可修改后续评审方式");
        }
        if (FlowableConstant.PRE_REVIEW_OFFICER.equals(stage)) {
            archReviewInfoDao.deleteById(id);
        }
        archReviewInfoDao.saveOrUpdate(archReviewInfo);
    }

    @Override
    public void updateArchReviewState(Long id, ArchReviewState state) {
        ArchReviewInfo archReviewInfo = getById(id);
        Assert.notNull(archReviewInfo, "未找到架构评审信息");
        archReviewInfo.setState(state);
        archReviewInfo.setProcessFinishTime(ESUtil.getNumberDateTime());
        archReviewInfoDao.saveOrUpdate(archReviewInfo);
    }

    @Override
    public ArchReviewInfo getArchReviewByProcessInstanceId(String processInstanceId) {
        TermQueryBuilder query = QueryBuilders.termQuery("processInstanceId.keyword", processInstanceId);
        List<ArchReviewInfo> archReviewInfos = archReviewInfoDao.getListByQuery(query);
        return CollectionUtils.isEmpty(archReviewInfos) ? null : archReviewInfos.get(0);
    }

    @Override
    public void updateAcceptTimeIfEmpty(Long id) {
        ArchReviewInfo archReviewInfo = getById(id);
        Assert.notNull(archReviewInfo, "未找到架构评审信息");
        if (archReviewInfo.getAcceptTime() != null) {
            return;
        }
        archReviewInfo.setAcceptTime(ESUtil.getNumberDateTime(new Date()));
        archReviewInfoDao.saveOrUpdate(archReviewInfo);
    }

    @Override
    public void updatePreReviewOfficer(Long id, String preReviewOfficer) {
        ArchReviewInfo archReviewInfo = getById(id);
        Assert.notNull(archReviewInfo, "未找到架构评审信息");
        archReviewInfo.setPreReviewOfficer(preReviewOfficer);
        archReviewInfoDao.saveOrUpdate(archReviewInfo);
    }

    @Override
    public ArchReviewApproveInfo approveInfo(String taskId) {
        if (StringUtils.isBlank(taskId)) {
            throw new BinaryException("任务id不能为空");
        }
        TaskResponse taskResponse = flowableFeign.getTaskInfoByTaskId(taskId);
        Assert.notNull(taskResponse, "未找到任务信息");
        Long id = Long.valueOf(taskResponse.getBusinessKey());
        ArchReviewApproveInfo approveInfo = commonApproveInfo(id);
        //获取“后续评审方式”推荐值
        FeatureReviewType preferFeatureReviewType = getPreferFureReviewType(taskResponse, getById(id));
        if (preferFeatureReviewType != null) {
            approveInfo.setFeatureReviewType(preferFeatureReviewType);
        }
        approveInfo.setTaskEnd(taskResponse.getTaskEndTime() != null);
        setPMOReveiewType(approveInfo, taskResponse);
        //度量测评
        approveInfo.setMeasureEvaluate(measureEvaluateService.detail(taskId));
        return approveInfo;
    }

    private void setPMOReveiewType(ArchReviewApproveInfo approveInfo, TaskResponse taskResponse) {
        //非pmo节点
        if (!FlowableConstant.PMO_REVIEW.equals(taskResponse.getDescription())) {
            return;
        }
        ArchReviewInfo reviewInfo = getById(approveInfo.getId());
        //pmo待办或pmo审核通过已办
        if (taskResponse.getTaskEndTime() == null) {
            approveInfo.setPmoReviewType(reviewInfo.getFeatureReviewType());
            return;
        }
        Map<String, Object> taskEchoVariables = taskResponse.getTaskEchoVariables();
        if (!CollectionUtils.isEmpty(taskEchoVariables) && "pass".equals(taskEchoVariables.get("pass"))) {
            approveInfo.setPmoReviewType(approveInfo.getFeatureReviewType());
        }
    }

    @Override
    public ArchReviewApproveInfo approveInfo(Long id) {
        ArchReviewApproveInfo approveInfo = commonApproveInfo(id);
        approveInfo.setMeasureEvaluate(measureEvaluateService.detail(getById(id)));

        ArchReviewInfo reviewInfo = getById(id);
        /**
         * pmo审核形式
         * 【【方案评审页面】驳回时增加可修改字段+“PMO审核形式”字段展示】
         * https://www.tapd.cn/65990798/prong/stories/view/1165990798001128596
         */
        if (ArchReviewState.REVIEWING.equals(reviewInfo.getState())) {
            ActiveTaskQueryReq activeTaskQueryReq = new ActiveTaskQueryReq();
            activeTaskQueryReq.setProcessInstanceId(approveInfo.getProcessInstanceId());
            activeTaskQueryReq.setDescriptions(Collections.singletonList(FlowableConstant.PMO_REVIEW));
            List<TaskResponse> taskResponses = flowableFeign.queryActiveTaskByProcIdAndDes(activeTaskQueryReq);
            if (!CollectionUtils.isEmpty(taskResponses)) {
                approveInfo.setPmoReviewType(reviewInfo.getFeatureReviewType());
            }
        }
        if (ArchReviewState.PASS.equals(reviewInfo.getState())) {
            approveInfo.setPmoReviewType(reviewInfo.getFeatureReviewType());
        }
        return approveInfo;
    }

    public ArchReviewApproveInfo commonApproveInfo(Long id) {
        ArchReviewInfo reviewInfo = getById(id);
        Assert.notNull(reviewInfo, "未找到架构评审信息");
        PorcessResponse porcessResponse = flowableFeign.getProcessInstanceByProcessInstanceId(reviewInfo.getProcessInstanceId());
        Assert.notNull(porcessResponse, "未找到流程实例信息");
        ArchReviewApproveInfo approveInfo = new ArchReviewApproveInfo();
        approveInfo.setProcessEnd(!FLOWSTATUS.ACTIVE.equals(porcessResponse.getStatus()));
        approveInfo.setId(id);
        approveInfo.setProcessInstanceId(reviewInfo.getProcessInstanceId());
        approveInfo.setCreateTime(transDateFormat(reviewInfo.getCreateTime(), "yyyy/MM/dd HH:mm:ss"));
        approveInfo.setCreatorCode(reviewInfo.getCreatorCode());
        UserInfo creator = getUserInfoByLoginCode(reviewInfo.getCreatorCode());
        approveInfo.setCreatorName(creator == null ? approveInfo.getCreatorCode() : creator.getUserName());
        approveInfo.setCreatorIcon(creator == null ? null : creator.getIcon());
        String creatorOrgName = "-";
        if (creator != null && !CollectionUtils.isEmpty(creator.getOrgs())) {
            List<SysOrg> orgs = new ArrayList<>(creator.getOrgs());
            orgs.sort(Comparator.comparing(SysOrg::getId));
            creatorOrgName = orgs.iterator().next().getOrgName();
        }
        approveInfo.setCreatorOrgName(creator == null ? "-" : creatorOrgName);
        approveInfo.setReviewType(reviewInfo.getReviewType());
        approveInfo.setItemType(reviewInfo.getItemType());
        if (ItemType.PROJECT.equals(approveInfo.getItemType())) {
            approveInfo.setProjectCiCode(reviewInfo.getProjectCiCode());
            List<SimpProjectInfo> projectInfos = projectService.queryByCiCodes(Arrays.asList(reviewInfo.getProjectCiCode()));
            String projectName = reviewInfo.getProjectInfo().getLabel();
            if (!CollectionUtils.isEmpty(projectInfos)) {
                projectName = projectInfos.get(0).getLabel();
            }
            approveInfo.setProjectName(projectName);
        }
        if (ItemType.SYSTEM.equals(approveInfo.getItemType())) {
            approveInfo.setSysCiCodes(reviewInfo.getSysCiCodes());
            String sysNames = reviewInfo.getSystemInfos().stream().map(
                    SimpSystemInfo::getLabel).collect(Collectors.joining(","));
            List<SimpSystemInfo> systemInfos = systemService.queryByCiCodes(reviewInfo.getSysCiCodes());
            if (!CollectionUtils.isEmpty(systemInfos)) {
                sysNames = systemInfos.stream().map(SimpSystemInfo::getLabel).collect(Collectors.joining(","));
            }
            approveInfo.setSystemName(sysNames);
        }
        approveInfo.setBuildType(reviewInfo.getBuildType());
        approveInfo.setBuildMode(reviewInfo.getBuildMode());
        approveInfo.setCustomizeSys(reviewInfo.getCustomizeSys());
        approveInfo.setIncludeBizTrans(reviewInfo.getIncludeBizTrans());
        approveInfo.setIncludeArchTrans(reviewInfo.getIncludeArchTrans());
        approveInfo.setTransLevel(reviewInfo.getTransLevel());
        approveInfo.setProjectManager(reviewInfo.getProjectManager());
        SysUser projectManager = getUserByLoginCode(reviewInfo.getProjectManager());
        approveInfo.setProjectManagerName(projectManager == null ? approveInfo.getProjectManagerName() : projectManager.getUserName());
        approveInfo.setBelongOrgId(reviewInfo.getBelongOrgId());
        String belongOrgName = reviewInfo.getBelongOrgName();
        SysOrg sysOrg = getOrgById(reviewInfo.getBelongOrgId());
        if (sysOrg != null) {
            SysOrg parentOrg = getOrgById(sysOrg.getParentOrgId());
            if (parentOrg != null) {
                belongOrgName = parentOrg.getOrgName().concat("/").concat(sysOrg.getOrgName());
            }
        }
        approveInfo.setBelongOrgName(belongOrgName);
        approveInfo.setRefLink(StringUtils.isBlank(reviewInfo.getRefLink()) ? "-" : reviewInfo.getRefLink());
        approveInfo.setReviewDesc(reviewInfo.getReviewDesc());
        approveInfo.setAcceptTime(transDateFormat(reviewInfo.getAcceptTime(), "yyyy/MM/dd HH:mm:ss"));
        List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(reviewInfo.getPlanIds());
        if (CollectionUtils.isEmpty(plans)) {
            throw new BinaryException("未查到评审方案信息");
        }
        List<Long> planTemplateTypeIds = plans.stream().map(PlanDesignInstance::getTypeId)
                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
        Map<Long, TemplateType> templateTypeMap = new ConcurrentHashMap<>();
        if (!CollectionUtils.isEmpty(planTemplateTypeIds)) {
            List<TemplateType> templateTypes = deliverableTemplateService.getTemplateTypeByIds(planTemplateTypeIds);
            if (!CollectionUtils.isEmpty(templateTypes)) {
                templateTypeMap = templateTypes.stream().collect(Collectors.toMap(TemplateType::getId, Function.identity()));
            }
        }

        Set<String> planCreators = plans.stream().map(PlanDesignInstance::getCreatorCode).collect(Collectors.toSet());
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodes(planCreators.toArray(new String[planCreators.size()]));
        List<SysUser> sysUsers = userApiSvc.getSysUserByCdt(cdt);
        Map<String, SysUser> planCreatorMap = new ConcurrentHashMap<>();
        if (!CollectionUtils.isEmpty(sysUsers)) {
            planCreatorMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getLoginCode, Function.identity()));
        }

        List<FlowPlanInfo> planInfos = new ArrayList<>();
        for (PlanDesignInstance plan : plans) {
            FlowPlanInfo flowPlanInfo = new FlowPlanInfo();
            flowPlanInfo.setId(plan.getId());
            flowPlanInfo.setName(plan.getName());
            flowPlanInfo.setTypeName("-");
            if (plan.getTypeId() != null && templateTypeMap.containsKey(plan.getTypeId())) {
                flowPlanInfo.setTypeName(templateTypeMap.get(plan.getTypeId()).getTypeName());
            }
            flowPlanInfo.setCreatorName(planCreatorMap.containsKey(plan.getCreatorCode()) ?
                    planCreatorMap.get(plan.getCreatorCode()).getUserName() : plan.getCreatorCode());
            flowPlanInfo.setBindAssetName(getAssetName(plan));
            String sysNames = "-";
            if (ItemType.PROJECT.equals(reviewInfo.getItemType()) && !CollectionUtils.isEmpty(plan.getSystemCiCodes())) {
                List<SimpSystemInfo> systemInfos = systemService.queryByCiCodes(plan.getSystemCiCodes());
                if (!CollectionUtils.isEmpty(systemInfos)) {
                    sysNames = systemInfos.stream().map(SimpSystemInfo::getLabel).collect(Collectors.joining(","));
                }
            }
            flowPlanInfo.setSysNames(sysNames);
            flowPlanInfo.setEchoDirName(plan.getEchoDirName());
            planInfos.add(flowPlanInfo);
        }
        approveInfo.setPlanIds(reviewInfo.getPlanIds());
        approveInfo.setPlanInfos(planInfos);
        approveInfo.setFeatureReviewType(reviewInfo.getFeatureReviewType());
        approveInfo.setTitle(getTitle(reviewInfo));
        return approveInfo;
    }

    private FeatureReviewType getPreferFureReviewType(TaskResponse taskResponse, ArchReviewInfo reviewInfo) {
        //预评审-值班人评审节点
        if (FlowableConstant.PRE_REVIEW_OFFICER.equals(taskResponse.getDescription())) {
            if (ArchReviewType.APPROVAL.equals(reviewInfo.getReviewType())) {
                //(1)立项审批-预评审-值班人评审节点，“后续评审方式”推荐值：
                //   非定制化系统：
                //      1)	非定制化系统，推荐“后续评审方案”为“PMO审核-邮件评审”的情形：
                //          i.	非 “新建系统”、且 不涉及 架构变更、且 不涉及 业务改造
                //      2)	非定制化系统，推荐“后续评审方案”为“PMO审核--组织评审会议”的情形：
                //          i.	非 “新建系统”、且 不涉及 架构变更、且 涉及 业务改造
                //      3)	其他情况，推荐“后续评审方案”为“预评审-专家评审”
                //   定制化系统，系统推荐“后续评审方案”为“PMO审核-邮件评审”
                if (!reviewInfo.getCustomizeSys()) {
                    if (!BuildType.NEW_SYS.equals(reviewInfo.getBuildType()) && !reviewInfo.getIncludeArchTrans()) {
                        return !reviewInfo.getIncludeBizTrans() ? FeatureReviewType.PMO_MAIL : FeatureReviewType.PMO_ORG;
                    }
                    return FeatureReviewType.EXPERT;
                }
                return FeatureReviewType.PMO_MAIL;
            }
            //(2)架构变更-预评审-值班人评审节点，“后续评审方式”推荐值：
            //   非定制化系统：
            //      1)	只涉及简单架构变更，系统推荐“后续评审方案”为“PMO审核-邮件评审”
            //      2)	其他情形：系统推荐“后续评审方案”为“预评审-专家评审”
            //   定制化系统：
            //      1)	“普通变更”，系统推荐“后续评审方案”为“PMO审核-邮件评审”
            //      2)	“重要变更”，系统推荐“后续评审方案”为“预评审-专家评审”
            //      3)	其他情况，推荐“后续评审方案”为“PMO审核-邮件评审”
            if (!reviewInfo.getCustomizeSys()) {
                if (reviewInfo.getIncludeArchTrans() && TransLevel.SIMPLE_ARCH_TRANS.equals(reviewInfo.getTransLevel())) {
                    return FeatureReviewType.PMO_MAIL;
                }
                return FeatureReviewType.EXPERT;
            }
            if (reviewInfo.getIncludeArchTrans() && TransLevel.NOR_TRANS.equals(reviewInfo.getTransLevel())) {
                return FeatureReviewType.PMO_MAIL;
            }
            if (reviewInfo.getIncludeArchTrans() && TransLevel.IMP_TRANS.equals(reviewInfo.getTransLevel())) {
                return FeatureReviewType.EXPERT;
            }
            return FeatureReviewType.PMO_MAIL;
        }
        //预评审-终审节点，“后续评审方式”推荐值：“PMO审核--组织评审会议”
        if (FlowableConstant.PRE_REVIEW_FINAL_EXPERT.equals(taskResponse.getDescription())) {
            return FeatureReviewType.PMO_ORG;
        }
        return null;
    }

    private String getAssetName(PlanDesignInstance plan) {
        if (CollectionUtils.isEmpty(plan.getCiCodeList())) {
            return "-";
        }
        List<ESCIInfo> ciInfoList = ciSwitchSvc.getCiByCodes(plan.getCiCodeList(), null, LibType.DESIGN);
        List<String> ciLabelList = ciInfoList.stream().filter(esciInfo -> !BinaryUtils.isEmpty(esciInfo.getCiLabel()))
                .map(esciInfo -> esciInfo.getCiLabel().replace("[", "").replace("]", "").replaceAll("\"", ""))
                .collect(Collectors.toList());
        return CollectionUtils.isEmpty(ciLabelList) ? "-" : StringUtils.join(ciLabelList, "-");
    }

    private String transDateFormat(Long date, String format) {
        if (date == null) {
            return "-";
        }
        SimpleDateFormat timeFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        try {
            return sdf.format(timeFormatter.parse(date.toString()));
        } catch (ParseException e) {
            log.error("时间格式转化失败:date:{},format:{}", date, format);
        }
        return "-";
    }

    @Override
    public void saveOrUpdate(ArchReviewInfo archReviewInfo) {
        archReviewInfoDao.saveOrUpdate(archReviewInfo);
    }

    @Override
    public Page<ArchReviewRes> page(ArchReviewQueryReq req) {
        QueryBuilder query = getArchReviewPageQuery(req);
        List<SortBuilder<?>> sorts = new ArrayList<>();
        FieldSortBuilder createTimeSort = SortBuilders.fieldSort("createTime").order(SortOrder.DESC);
        FieldSortBuilder acceptTimeSort = SortBuilders.fieldSort("acceptTime").order(SortOrder.DESC);
        sorts.add(createTimeSort);
        sorts.add(acceptTimeSort);
        Page<ArchReviewInfo> page = archReviewInfoDao.getSortListByQuery(req.getPageNum(), req.getPageSize(), query, sorts);
        if (CollectionUtils.isEmpty(page.getData())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0, 0, new ArrayList<>());
        }
        ArchReviewPageContext context = parparePageContext(page.getData());
        List<ArchReviewRes> results = new ArrayList<>();
        for (ArchReviewInfo info : page.getData()) {
            ArchReviewRes result = new ArchReviewRes();
            result.setId(info.getId());
            result.setReviewType(info.getReviewType());
            result.setItemType(info.getItemType());
            if (ItemType.PROJECT.equals(info.getItemType())) {
                String projectName = context.getProjectInfoMap().containsKey(info.getProjectCiCode()) ?
                        context.getProjectInfoMap().get(info.getProjectCiCode()).getLabel() : info.getProjectInfo().getLabel();
                result.setItemName(projectName);
            }
            if (ItemType.SYSTEM.equals(info.getItemType())) {
                String sysNames = info.getSystemInfos().stream().map(SimpSystemInfo::getLabel).collect(Collectors.joining(","));
                List<SimpSystemInfo> systemInfos = new ArrayList<>();
                for (String sysCiCode : info.getSysCiCodes()) {
                    if (context.getSystemInfoMap().containsKey(sysCiCode)) {
                        systemInfos.add(context.getSystemInfoMap().get(sysCiCode));
                    }
                }
                if (!CollectionUtils.isEmpty(systemInfos)) {
                    sysNames = systemInfos.stream().map(SimpSystemInfo::getLabel).collect(Collectors.joining(","));
                }
                result.setItemName(sysNames);
            }
            result.setBelongOrgId(info.getBelongOrgId());
            String belongOrgName = info.getBelongOrgName();
            if (context.getOrgMap().containsKey(info.getBelongOrgId())) {
                SysOrg org = context.getOrgMap().get(info.getBelongOrgId());
                if (org.getParentOrgId() != null && context.getOrgMap().containsKey(org.getParentOrgId())) {
                    SysOrg parentOrg = context.getOrgMap().get(org.getParentOrgId());
                    belongOrgName = parentOrg.getOrgName().concat("/").concat(org.getOrgName());
                }
            }
            result.setBelongOrgName(belongOrgName);
            result.setAcceptTime(transDateFormat(info.getAcceptTime(), "yyyy-MM-dd"));
            result.setCreateTime(transDateFormat(info.getCreateTime(), "yyyy-MM-dd"));
            result.setPreReviewOfficer(info.getPreReviewOfficer());
            String preReviewOfficerName = "-";
            if (StringUtils.isNotBlank(info.getPreReviewOfficer())) {
                preReviewOfficerName = context.getUserMap().containsKey(info.getPreReviewOfficer()) ?
                        context.getUserMap().get(info.getPreReviewOfficer()).getUserName() : info.getPreReviewOfficer();
            }
            result.setPreReviewOfficerName(preReviewOfficerName);
            result.setState(info.getState());
            //当前节点
            String stage = "-";
            if (StringUtils.isNotBlank(info.getProcessInstanceId()) &&
                    context.getActiveTaskMap().containsKey(info.getProcessInstanceId())) {
                List<TaskResponse> tasks = context.getActiveTaskMap().get(info.getProcessInstanceId());
                List<String> names = new ArrayList<>();
                for (TaskResponse task : tasks) {
                    String name = getStageName(task);
                    if (StringUtils.isNotBlank(name) && !names.contains(name)) {
                        names.add(name);
                    }
                }
                if (!CollectionUtils.isEmpty(names)) {
                    stage = String.join(",", names);
                }
            }
            result.setStage(stage);
            result.setExpertReviewType(info.getExpertReviewType() == null ? info.getRotaReviewType() : info.getExpertReviewType());
            result.setProcessFinishTime(info.getProcessFinishTime());
            result.setProcessFinishTimeFormat(transDateFormat(info.getProcessFinishTime(), "yyyy-MM-dd"));
            result.setBuildType(info.getBuildType());
            result.setBuildMode(info.getBuildMode());
            result.setProjectManager(info.getProjectManager());
            String projectManagerName = info.getProjectManagerName();
            if (StringUtils.isNotBlank(info.getProjectManager())
                    && context.getUserMap().containsKey(info.getProjectManager())) {
                projectManagerName = context.getUserMap().get(info.getProjectManager()).getUserName();
            }
            result.setProjectManagerName(projectManagerName);
            result.setCreatorCode(info.getCreatorCode());
            String creatorName = info.getCreatorName();
            if (StringUtils.isNotBlank(info.getCreatorCode()) &&  context.getUserMap().containsKey(info.getCreatorCode())) {
                creatorName =  context.getUserMap().get(info.getCreatorCode()).getUserName();
            }
            result.setCreatorName(creatorName);
            result.setProcessInstanceId(info.getProcessInstanceId());
            result.setPlanIds(info.getPlanIds());
            results.add(result);
        }
        return new Page<>(req.getPageNum(), req.getPageSize(), page.getTotalRows(), page.getTotalPages(), results);
    }

    private String getStageName(TaskResponse task) {
        if (StringUtils.isBlank(task.getDescription())) {
            return null;
        }
        String description = task.getDescription();
        if (Arrays.asList(FlowableConstant.ORG_COUNTER_SIGN, FlowableConstant.ORG_OR_SIGN).contains(description)) {
            return "团队架构评审";
        }
        if (Objects.equals(FlowableConstant.PRE_REVIEW_OFFICER, description)) {
            return "预评审-值班人评审";
        }
        if (Objects.equals(FlowableConstant.PRE_REVIEW_ROTA, description)) {
            return "预评审-值班复核";
        }
        if (Objects.equals(FlowableConstant.PRE_REVIEW_EXPERT, description)) {
            return "预评审-专家初审";
        }
        if (Objects.equals(FlowableConstant.PRE_REVIEW_FINAL_EXPERT, description)) {
            return "预评审-终审";
        }
        if (Objects.equals(FlowableConstant.PMO_REVIEW, description)) {
            return "PMO审核";
        }
        List<String> rejects = Arrays.asList(
                FlowableConstant.ORG_COUNTER_SIGN_REJECT,
                FlowableConstant.ORG_OR_SIGN_REJECT,
                FlowableConstant.PRE_REVIEW_OFFICER_REJECT,
                FlowableConstant.PRE_REVIEW_ROTA_REJECT,
                FlowableConstant.PRE_REVIEW_EXPERT_REJECT,
                FlowableConstant.PRE_REVIEW_FINAL_EXPERT_REJECT,
                FlowableConstant.PMO_REVIEW_REJCET);
        if (rejects.contains(description)) {
            return "退回至申请人";
        }
        if (Objects.equals(FlowableConstant.START_USER_SUBMIT, description)) {
            return "流程创建";
        }
        return null;
    }

    private ArchReviewPageContext parparePageContext(List<ArchReviewInfo> archReviewInfos) {
        Set<String> projectCiCodes = new HashSet<>();
        Set<String> systemCiCodes = new HashSet<>();
        Set<Long> belongOrgIds = new HashSet<>();
        Set<String> userLoginCodes = new HashSet<>();
        List<String> activeProcessInstanceIds = new ArrayList<>();
        for (ArchReviewInfo archReviewInfo : archReviewInfos) {
            if (ItemType.SYSTEM.equals(archReviewInfo.getItemType())) {
                systemCiCodes.addAll(archReviewInfo.getSysCiCodes());
            }
            if (ItemType.PROJECT.equals(archReviewInfo.getItemType())) {
                projectCiCodes.add(archReviewInfo.getProjectCiCode());
            }
            belongOrgIds.add(archReviewInfo.getBelongOrgId());
            if (StringUtils.isNotBlank(archReviewInfo.getPreReviewOfficer())) {
                userLoginCodes.add(archReviewInfo.getPreReviewOfficer());
            }
            if (StringUtils.isNotBlank(archReviewInfo.getProjectManager())) {
                userLoginCodes.add(archReviewInfo.getProjectManager());
            }
            if (StringUtils.isNotBlank(archReviewInfo.getCreatorCode())) {
                userLoginCodes.add(archReviewInfo.getCreatorCode());
            }
            if (StringUtils.isNotBlank(archReviewInfo.getProcessInstanceId())
                    && ArchReviewState.REVIEWING.equals(archReviewInfo.getState())) {
                activeProcessInstanceIds.add(archReviewInfo.getProcessInstanceId());
            }
        }
        Map<String, SimpProjectInfo> projectInfoMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(projectCiCodes)) {
            List<SimpProjectInfo> projectInfos = projectService.queryByCiCodes(new ArrayList<>(projectCiCodes));
            if (!CollectionUtils.isEmpty(projectInfos)) {
                projectInfoMap = projectInfos.stream().collect(Collectors.toMap(SimpProjectInfo::getCiCode, Function.identity()));
            }
        }
        Map<String, SimpSystemInfo> systemInfoMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(systemCiCodes)) {
            List<SimpSystemInfo> systemInfos = systemService.queryByCiCodes(new ArrayList<>(systemCiCodes));
            if (!CollectionUtils.isEmpty(systemInfos)) {
                systemInfoMap = systemInfos.stream().collect(Collectors.toMap(SimpSystemInfo::getCiCode, Function.identity()));
            }
        }
        Map<Long, SysOrg> orgMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(belongOrgIds)) {
            List<SysOrg> orgs = getOrgByIds(belongOrgIds);
            if (!CollectionUtils.isEmpty(orgs)) {
                orgMap = orgs.stream().collect(Collectors.toMap(SysOrg::getId, Function.identity()));
                //上级组织
                Set<Long> parentOrgIds = orgs.stream().map(SysOrg::getParentOrgId).collect(Collectors.toSet());
                if (!CollectionUtils.isEmpty(parentOrgIds)) {
                    List<SysOrg> parentOrgs = getOrgByIds(parentOrgIds);
                    if (!CollectionUtils.isEmpty(parentOrgs)) {
                        orgMap.putAll(parentOrgs.stream().collect(Collectors.toMap(SysOrg::getId, Function.identity())));
                    }
                }
            }
        }
        Map<String, SysUser> userMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(userLoginCodes)) {
            List<SysUser> users = getUserByLoginCodes(userLoginCodes);
            if (!CollectionUtils.isEmpty(users)) {
                userMap = users.stream().collect(Collectors.toMap(SysUser::getLoginCode, Function.identity()));
            }
        }
        Map<String, List<TaskResponse>> activeTaskMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(activeProcessInstanceIds)) {
            List<TaskResponse> tasks = flowableFeign.getActiveSimpleTaskByProcessInstanceIds(activeProcessInstanceIds);
            if (!CollectionUtils.isEmpty(tasks)) {
                tasks.sort(Comparator.comparing(TaskResponse::getTaskCreateTime));
                activeTaskMap = tasks.stream().collect(Collectors.groupingBy(TaskResponse::getProcessInstanceId));
            }
        }
        ArchReviewPageContext pageContext = new ArchReviewPageContext();
        pageContext.setProjectInfoMap(projectInfoMap);
        pageContext.setSystemInfoMap(systemInfoMap);
        pageContext.setOrgMap(orgMap);
        pageContext.setUserMap(userMap);
        pageContext.setActiveTaskMap(activeTaskMap);
        return pageContext;
    }

    private List<SysUser> getUserByLoginCodes(Set<String> loginCodes) {
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodes(loginCodes.toArray(new String[loginCodes.size()]));
        return userApiSvc.getSysUserByCdt(cdt);
    }

    private List<SysOrg> getOrgByIds(Set<Long> orgIds) {
        CSysOrg query = new CSysOrg();
        query.setIds(orgIds.toArray(new Long[orgIds.size()]));
        return orgService.queryListByCdt(query);
    }

    private QueryBuilder getArchReviewPageQuery(ArchReviewQueryReq req) {
        Assert.notNull(req, "分页参数不能为空");
        Assert.notNull(req.getPageNum(), "第几页不能为空");
        Assert.notNull(req.getPageSize(), "每页显示记录数不能为空");
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        if (StringUtils.isNotBlank(req.getItemName())) {
            ProjectQueryVo projectQueryVo = new ProjectQueryVo();
            projectQueryVo.setPageNum(1);
            projectQueryVo.setPageSize(9999);
            projectQueryVo.setKeyword(req.getItemName());
            Page<SimpProjectInfo> projectPage = projectService.list(projectQueryVo);

            SystemQueryVo systemQueryVo = new SystemQueryVo();
            systemQueryVo.setPageNum(1);
            systemQueryVo.setPageSize(9999);
            systemQueryVo.setKeyword(req.getItemName());
            Page<SimpSystemInfo> systemPage = systemService.list(systemQueryVo);
            if (CollectionUtils.isEmpty(projectPage.getData()) && CollectionUtils.isEmpty(systemPage.getData())) {
                return QueryBuilders.termQuery("id", -1);
            }
            BoolQueryBuilder itemNameQuery = QueryBuilders.boolQuery();
            if (!CollectionUtils.isEmpty(projectPage.getData())) {
                Set<String> projectCiCodes = projectPage.getData().stream().map(SimpProjectInfo::getCiCode).collect(Collectors.toSet());
                BoolQueryBuilder projectQuery = QueryBuilders.boolQuery();
                projectQuery.must(QueryBuilders.termQuery("itemType.keyword", ItemType.PROJECT));
                projectQuery.must(QueryBuilders.termsQuery("projectCiCode.keyword", projectCiCodes));
                itemNameQuery.should(projectQuery);
            }
            if (!CollectionUtils.isEmpty(systemPage.getData())) {
                Set<String> systemCiCodes = systemPage.getData().stream().map(SimpSystemInfo::getCiCode).collect(Collectors.toSet());
                BoolQueryBuilder projectQuery = QueryBuilders.boolQuery();
                projectQuery.must(QueryBuilders.termQuery("itemType.keyword", ItemType.SYSTEM));
                projectQuery.must(QueryBuilders.termsQuery("systemInfos.ciCode.keyword", systemCiCodes));
                itemNameQuery.should(projectQuery);
            }
            query.must(itemNameQuery);
        }
        if (StringUtils.isNotBlank(req.getAcceptTimeBegin()) || StringUtils.isNotBlank(req.getAcceptTimeEnd())) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("acceptTime");
            if (StringUtils.isNotBlank(req.getAcceptTimeBegin())) {
                Long timeBeginLong = ESUtil.getNumberDateTime(getDate(req.getAcceptTimeBegin()));
                rangeQuery.gte(timeBeginLong);
            }
            if (StringUtils.isNotBlank(req.getAcceptTimeEnd())) {
                Long timeEndLong = ESUtil.getNumberDateTime(getDate(req.getAcceptTimeEnd()));
                rangeQuery.lte(timeEndLong);
            }
            query.must(rangeQuery);
        }
        if (!BinaryUtils.isEmpty(req.getProcessFinishTimeBegin())) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("processFinishTime");
            rangeQuery.gte(req.getProcessFinishTimeBegin());
            query.must(rangeQuery);
        }
        if (req.getBuildType() != null) {
            query.must(QueryBuilders.termQuery("buildType.keyword", req.getBuildType()));
        }
        if (req.getBuildMode() != null) {
            query.must(QueryBuilders.termQuery("buildMode.keyword", req.getBuildMode()));
        }
        if (!CollectionUtils.isEmpty(req.getStates())) {
            query.must(QueryBuilders.termsQuery("state.keyword", req.getStates()));
        }
        if (!CollectionUtils.isEmpty(req.getStages())) {
            List<String> activeDescriptions = new ArrayList<>();
            for (ArchReviewRangeStage stage : req.getStages()) {
                List<String> descriptions = getActiveDescriptions(stage);
                if (!CollectionUtils.isEmpty(descriptions)) {
                    activeDescriptions.addAll(descriptions);
                }
            }
            ArchReviewTaskQueryReq taskQueryReq = new ArchReviewTaskQueryReq();
            taskQueryReq.setType(1);
            taskQueryReq.setActiveDescriptions(activeDescriptions);
            List<TaskResponse> tasks = flowableFeign.listForArchReview(taskQueryReq);
            if (CollectionUtils.isEmpty(tasks)) {
                return QueryBuilders.termQuery("id", -1);
            }
            Set<String> processInstanceIds = tasks.stream().map(TaskResponse::getProcessInstanceId).filter(Objects::nonNull).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(processInstanceIds)) {
                return QueryBuilders.termQuery("id", -1);
            }
            query.must(QueryBuilders.termsQuery("processInstanceId.keyword", processInstanceIds));
        }
        if (req.getPmoReviewType() != null) {
            BoolQueryBuilder pmoReviewTypeQuery = QueryBuilders.boolQuery();
            pmoReviewTypeQuery.should(QueryBuilders.termQuery("expertReviewType.keyword", req.getPmoReviewType()));
            pmoReviewTypeQuery.should(QueryBuilders.termQuery("rotaReviewType.keyword", req.getPmoReviewType()));
            query.must(pmoReviewTypeQuery);
        }
        if (req.getFlowType() != null) {
            String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
            if (ArchReviewFlowType.START_BY_ME.equals(req.getFlowType())) {
                query.must(QueryBuilders.termQuery("creatorCode.keyword", curUserLoginCode));
            }
            if (ArchReviewFlowType.TO_DO.equals(req.getFlowType())
                    || ArchReviewFlowType.DONE.equals(req.getFlowType())) {
                ArchReviewTaskQueryReq taskQueryReq = new ArchReviewTaskQueryReq();
                taskQueryReq.setType(ArchReviewFlowType.TO_DO.equals(req.getFlowType()) ? 2 : 3);
                taskQueryReq.setAssignee(curUserLoginCode);
                List<TaskResponse> tasks = flowableFeign.listForArchReview(taskQueryReq);
                if (CollectionUtils.isEmpty(tasks)) {
                    return QueryBuilders.termQuery("id", -1);
                }
                Set<String> processInstanceIds = tasks.stream().map(TaskResponse::getProcessInstanceId).filter(Objects::nonNull).collect(Collectors.toSet());
                if (CollectionUtils.isEmpty(processInstanceIds)) {
                    return QueryBuilders.termQuery("id", -1);
                }
                query.must(QueryBuilders.termsQuery("processInstanceId.keyword", processInstanceIds));
            }
        }
        return query;
    }

    private List<String> getActiveDescriptions(ArchReviewRangeStage stage) {
        switch (stage) {
            case ORG_SIGN:
                return Arrays.asList(FlowableConstant.ORG_COUNTER_SIGN, FlowableConstant.ORG_OR_SIGN);
            case PRE_REVIEW_OFFICER:
                return Collections.singletonList(FlowableConstant.PRE_REVIEW_OFFICER);
            case PRE_REVIEW_ROTA:
                return Collections.singletonList(FlowableConstant.PRE_REVIEW_ROTA);
            case PRE_REVIEW_EXPERT:
                return Collections.singletonList(FlowableConstant.PRE_REVIEW_EXPERT);
            case PRE_REVIEW_FINAL_EXPERT:
                return Collections.singletonList(FlowableConstant.PRE_REVIEW_FINAL_EXPERT);
            case PMO:
                return Collections.singletonList(FlowableConstant.PMO_REVIEW);
            case REJECT_TO_SUBMITTER:
                return Arrays.asList(
                        FlowableConstant.ORG_COUNTER_SIGN_REJECT,
                        FlowableConstant.ORG_OR_SIGN_REJECT,
                        FlowableConstant.PRE_REVIEW_OFFICER_REJECT,
                        FlowableConstant.PRE_REVIEW_ROTA_REJECT,
                        FlowableConstant.PRE_REVIEW_EXPERT_REJECT,
                        FlowableConstant.PRE_REVIEW_FINAL_EXPERT_REJECT,
                        FlowableConstant.PMO_REVIEW_REJCET);
            case START_USER_SUBMIT:
                return Collections.singletonList(FlowableConstant.START_USER_SUBMIT);
            default:
                throw new BinaryException("不支持的节点");
        }
    }

    private Date getDate(String dateStr) {
        try {
            return STR_DF.parse(dateStr);
        } catch (ParseException e) {
            throw new BinaryException("日期转换错误:" + dateStr);
        }
    }

    @Override
    public void circulate(CirculateReq req) {
        Assert.notNull(req, "缺少参数");
        if (StringUtils.isBlank(req.getProcessInstanceId())) {
            throw new BinaryException("流程实例id不能为空");
        }
        if (CollectionUtils.isEmpty(req.getCirculators())) {
            throw new BinaryException("传阅人不能为空");
        }
        ArchReviewInfo reviewInfo = getArchReviewByProcessInstanceId(req.getProcessInstanceId());
        Assert.notNull(reviewInfo, "未找到架构评审信息");
        List<SysUser> circulators = getUserByLoginCodes(new HashSet<>(req.getCirculators()));
        if (CollectionUtils.isEmpty(circulators) || req.getCirculators().size() != circulators.size()) {
            throw new BinaryException("未找到传阅人，请刷新页面重试");
        }
        SysUser creator = getUserByLoginCode(SysUtil.getCurrentUserInfo().getLoginCode());
        Assert.notNull(creator, "未找到发起人，请刷新页面重试");
        String flowName = "传阅：".concat(getFlowName(reviewInfo));

        //List<ArchReviewCirCulateMsgVo> msgVos = new ArrayList<>();
        List<WorkbenchChargeDone> chargeDones = new ArrayList<>();
        List<CirCulateRecord> cirCulateRecords = new ArrayList<>();
        for (SysUser circulator : circulators) {
            //消息入参
            //ArchReviewCirCulateMsgVo msgVo = new ArchReviewCirCulateMsgVo();
            //msgVo.setArchReviewId(archReviewInfo.getId());
            //msgVo.setItemName(iteamName);
            //msgVo.setCreatorId(creator.getId());
            //msgVo.setCreatorName(creator.getUserName());
            //msgVo.setCirCulator(circulator.getLoginCode());
            //msgVo.setArchReviewId(msgVo.getArchReviewId());
            //msgVos.add(msgVo);

            //待办入参
            WorkbenchChargeDone done = new WorkbenchChargeDone();
            done.setType(6);
            done.setAction(1);
            done.setBusinessKey(reviewInfo.getId().toString());
            done.setProcessInstanceId(req.getProcessInstanceId());
            done.setProcessInstanceName(flowName);
            done.setUserId(circulator.getLoginCode());
            done.setTaskName("传阅");
            done.setSubmitter(creator.getLoginCode());
            done.setCreator(creator.getLoginCode());
            done.setTaskCreateTime(new Date());
            chargeDones.add(done);


            //传阅记录
            CirCulateRecord cirCulateRecord = new CirCulateRecord();
            cirCulateRecord.setId(ESUtil.getUUID());
            cirCulateRecord.setCirculator(circulator.getLoginCode());
            cirCulateRecord.setCirculatorName(circulator.getUserName());
            cirCulateRecord.setProcessInstanceId(req.getProcessInstanceId());
            cirCulateRecord.setCreatorCode(creator.getLoginCode());
            cirCulateRecord.setCreatorName(creator.getUserName());
            cirCulateRecord.setCreateTime(ESUtil.getNumberDateTime());
            cirCulateRecord.setModifierCode(creator.getLoginCode());
            cirCulateRecord.setModifierName(creator.getUserName());
            cirCulateRecord.setModifyTime(ESUtil.getNumberDateTime());
            cirCulateRecords.add(cirCulateRecord);
        }
        //发消息
        //noticeService.archReviewCirCulateMsgSave(msgVos);
        //发待办
        workbenchChargeDoneSvc.cirCulate(chargeDones);
        //传阅记录
        cirCulateRecordDao.saveOrUpdateBatch(cirCulateRecords);
    }

    @Override
    public Page<CirculateListRes> cirCulateList(CirculateListReq req) {
        Assert.notNull(req, "缺少参数");
        Assert.notNull(req.getPageNum(), "第几页不能为空");
        Assert.notNull(req.getPageSize(), "每页显示记录数不能为空");
        if (StringUtils.isBlank(req.getProcessInstanceId())) {
            throw new BinaryException("流程实例id不能为空");
        }
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("processInstanceId.keyword", req.getProcessInstanceId()));
        List<SortBuilder<?>> sorts = new ArrayList<>();
        FieldSortBuilder createTimeSortBuilder = SortBuilders.fieldSort("createTime").order(SortOrder.DESC);
        sorts.add(createTimeSortBuilder);
        Page<CirCulateRecord> page = cirCulateRecordDao.getSortListByQuery(req.getPageNum(), req.getPageSize(), query, sorts);
        if (CollectionUtils.isEmpty(page.getData())) {
            return new Page<>(req.getPageNum(), req.getPageSize(), 0, 0, new ArrayList<>());
        }
        Set<String> userLoginCodes = new HashSet<>();
        page.getData().forEach(record -> {
            userLoginCodes.add(record.getCirculator());
            userLoginCodes.add(record.getCreatorCode());
        });
        List<SysUser> sysUsers = getUserByLoginCodes(userLoginCodes);
        Map<String, SysUser> userMap = new ConcurrentHashMap<>();
        if (!CollectionUtils.isEmpty(sysUsers)) {
            userMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getLoginCode, Function.identity()));
        }
        List<CirculateListRes> circulates = new ArrayList<>();
        for (CirCulateRecord record : page.getData()) {
            CirculateListRes circulate = new CirculateListRes();
            circulate.setCirculator(record.getCirculator());
            circulate.setCirculatorName(userMap.containsKey(record.getCirculator()) ?
                    userMap.get(record.getCirculator()).getUserName() : record.getCirculatorName());
            circulate.setTime(transDateFormat(record.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
            circulate.setCreatorCode(record.getCreatorCode());
            circulate.setCreatorName(userMap.containsKey(record.getCreatorCode()) ?
                    userMap.get(record.getCreatorCode()).getUserName() : record.getCreatorName());
            circulates.add(circulate);
        }
        return new Page<>(req.getPageNum(), req.getPageSize(), page.getTotalRows(), page.getTotalPages(), circulates);
    }

    @Override
    public void finishCirculateJob(Long workbenchChargeDoneId) {
        workbenchChargeDoneSvc.finishCirCulate(workbenchChargeDoneId);
    }

    @Override
    public void updateProject(ItemType itemType, String projectCiCode, List<PlanDesignInstance> designPlans, ArchReviewState state, String reviewId, String reviewStartTime, String reviewEndTime) {
        if (ItemType.PROJECT.equals(itemType)) {
            try {
                ArchReviewConf archReviewConf = axeaConfService.getArchReviewConf();
                ESCISearchBean bean = new ESCISearchBean();
                bean.setCiCodes(Collections.singletonList(projectCiCode));
                bean.setClassIds(Arrays.asList(archReviewConf.getProjectClassId()));
                bean.setDomainId(1L);
                bean.setPageNum(1);
                bean.setPageSize(1);
                CiGroupPage page = ciSwitchSvc.queryPageBySearchBean(bean, false, LibType.DESIGN);
                if (CollectionUtils.isEmpty(page.getData())) {
                    log.error("updateProject 未查询到关联资产，ciCode：" + projectCiCode);
                    return;
                }
                /**
                 * 从视图配置中获取配置信息
                 */
                String config = bmConfigSvc.getConfigType("INTERFACE_DATA_CONFIG");
                if (StrUtil.isEmpty(config)) {
                    log.error("updateProject 无法获取视图配置项：INTERFACE_DATA_CONFIG");
                    return;
                }
                JSONObject jsonConfig = JSON.parseObject(config);
                JSONObject projectMap = jsonConfig.getJSONObject("projectMap");
                if (BinaryUtils.isEmpty(projectMap)) {
                    log.error("updateProject 无法获取视图配置项：projectMap");
                    return;
                }
                CcCiInfo ccCiInfo = page.getData().get(0);
                log.info("updateProject 更新评审状态：" + state);
                if (ArchReviewState.REVIEWING.equals(state)) {
                    ccCiInfo.getAttrs().put(projectMap.getString("archReviewStatus"),"评审中");
                } else if (ArchReviewState.PASS.equals(state)) {
                    ccCiInfo.getAttrs().put(projectMap.getString("archReviewStatus"),"评审完成-同意");
                } else if (ArchReviewState.NO_PASS.equals(state)) {
                    ccCiInfo.getAttrs().put(projectMap.getString("archReviewStatus"),"评审完成-不同意");
                } else if (ArchReviewState.CANCEL.equals(state)) {
                    ccCiInfo.getAttrs().put(projectMap.getString("archReviewStatus"),"撤销");
                } else if (ArchReviewState.TERM.equals(state)) {
                    ccCiInfo.getAttrs().put(projectMap.getString("archReviewStatus"),"终止");
                }
                Map<String, String> archPlanUrlMap = new HashMap<>();
                for (PlanDesignInstance designPlan : designPlans) {
                    archPlanUrlMap.put(designPlan.getName(), "/deliverable#/detail?planId=" + designPlan.getId() + "&isPlanDesignDetail=true");
                }
                ccCiInfo.getAttrs().put(projectMap.getString("archPlanUrl"), JSON.toJSONString(archPlanUrlMap));
                ccCiInfo.getAttrs().put(projectMap.getString("reviewId"), reviewId);
                ccCiInfo.getAttrs().put(projectMap.getString("reviewStartTime"), reviewStartTime);
                ccCiInfo.getAttrs().put(projectMap.getString("reviewEndTime"), reviewEndTime);
                SysUser loginUser = null;
                try {
                    loginUser = SysUtil.getCurrentUserInfo();
                } catch (Exception e) {
                    log.error("updateProject 获取登陆用户失败，取ci的创建人作为登录用户");
                }
                if (loginUser != null) {
                    ciSwitchSvc.saveOrUpdateCI(ccCiInfo, LibType.DESIGN);
                } else {
                    try {
                        String ownerCode = ccCiInfo.getCi().getOwnerCode();
                        BoolQueryBuilder must = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("loginCode.keyword", ownerCode));
                        List<SysUser> sysUsers = esUserSvc.getListByQuery(must);
                        if (BinaryUtils.isEmpty(sysUsers)) {
                            log.error("updateProject 未查询到用户负责人！用户名：" + ownerCode);
                            return;
                        }
                        SysUser sysUser = sysUsers.get(0);
                        boolean open = Local.isOpen();
                        try {
                            if(!open) {
                                SimpleUser user = new SimpleUser();
                                user.setId(sysUser.getId());
                                user.setDomainId(sysUser.getDomainId());
                                user.setLoginCode(sysUser.getLoginCode());
                                user.setUserName(sysUser.getUserName());
                                // 模拟当前登录用户
                                Local.open(user);
                            }
                            ciSwitchSvc.saveOrUpdateCI(ccCiInfo, LibType.DESIGN);
                            if(!open) Local.commit();
                        } catch(Throwable t) {
                            Local.rollback();
                            throw t;
                        } finally {
                            if(!open) Local.close();
                        }
                    } catch(Throwable t) {
                        log.error("updateProject 更新项目资产状态错误：" + t.getMessage());
                        return;
                    }
                }

            } catch (Exception exception) {
                log.error("updateProject 更新项目资产状态异常：" + exception.getMessage());
                return;
            }
        }

    }

    @Override
    public ArchReviewRes getState(String processInstanceId) {
        Assert.notNull(processInstanceId, "流程ID不能为空");
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("processInstanceId.keyword", processInstanceId));
        Page<ArchReviewInfo> page = archReviewInfoDao.getListByQuery(1, 10, query);
        ArchReviewRes result = new ArchReviewRes();
        if (!CollectionUtils.isEmpty(page.getData())) {
            result.setProcessInstanceId(processInstanceId);
            result.setState(page.getData().get(0).getState());
        }
        return result;
    }
}
